﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace InnerProduct
{
    public partial class ColorWheel : UserControl
    {
        public ColorWheel()
        {
            InitializeComponent();
            Wheel.Source = ColorWheelImage();
            Wheel.SizeChanged += new SizeChangedEventHandler(Wheel_SizeChanged);
        }

        ImageSource ColorWheelImage()
        {
            WriteableBitmap wb = new WriteableBitmap(160, 160);
            for (int x = 0; x < 160; x++)
            {
                for (int y = 0; y < 160; y++)
                {
                    double r = (x - 79.5) * (x - 79.5) + (y - 79.5) * (y - 79.5);
                    if (r < 6100)
                    {
                        ColorHSV hsv = CoordsToColor(x, y, 79.5, 79.5, 80);
                        Color rgb = hsv.Color;
                        wb.Pixels[y * 160 + x] = (255 << 24) | (rgb.R << 16) | (rgb.G << 8) | rgb.B;
                    }
                    else if (r < 6240)
                    {
                        ColorHSV hsv = CoordsToColor(x, y, 79.5, 79.5, 80);
                        Color rgb = hsv.Color;
                        rgb.A = (byte)(255 - ((float)(r - 6100) / (6240 - 6100)) * 256);

                        //pre-multiply alpha
                        double a = rgb.A / 255.0;
                        rgb.R = (byte)(rgb.R * a);
                        rgb.G = (byte)(rgb.G * a);
                        rgb.B = (byte)(rgb.B * a);
                        wb.Pixels[y * 160 + x] = (rgb.A << 24) | (rgb.R << 16) | (rgb.G << 8) | rgb.B;
                    }
                    else
                        wb.Pixels[y * 160 + x] = 0;
                }
            }
            wb.Invalidate();
            return wb;
        }

        protected static ColorHSV CoordsToColor(int x, int y, double cx, double cy, double R)
        {
            double dx = x - cx;
            double dy = -(y - cy);

            double r = Math.Sqrt(dx * dx + dy * dy);
            double s = r / R;
            if (s < 0.03)
                return new ColorHSV(0, 0, 1.0);
            if (s > 1) s = 1.0;

            double h = Math.Acos(dx / r) * (180 / Math.PI);
            if (dy < 0) h = 360 - h;

            return new ColorHSV(h, s, 1.0);
        }

        protected static Point HS2XY(double R, ColorHSV c)
        {
            double r = c.S * R;

            double dx = Math.Cos(c.H / (180 / Math.PI)) * r;
            double dy = Math.Sin(c.H / (180 / Math.PI)) * r;

            return new Point(dx, dy);
        }

        void Wheel_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            ChangeSelectorPosition();
        }

        private void ChangeSelectorPosition()
        {
            Point pt = HS2XY(Wheel.ActualHeight / 2 - 1, ColorHSV);
            Canvas.SetLeft(Selector, pt.X + ActualWidth / 2 - Selector.ActualWidth / 2);
            Canvas.SetTop(Selector, ActualHeight / 2 - pt.Y - Selector.ActualHeight / 2);
        }

        #region ColorHSV (DependencyProperty)

        /// <summary>
        /// HS-V color on HS color wheel
        /// </summary>
        public ColorHSV ColorHSV
        {
            get { return (ColorHSV)GetValue(ColorHSVProperty); }
            set { SetValue(ColorHSVProperty, value); }
        }
        public static readonly DependencyProperty ColorHSVProperty =
            DependencyProperty.Register("ColorHSV", typeof(ColorHSV), typeof(ColorWheel),
            new PropertyMetadata(new ColorHSV(0,0,1.0), new PropertyChangedCallback(OnColorHSVChanged)));

        private static void OnColorHSVChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ColorWheel)d).OnColorHSVChanged(e);
        }

        protected virtual void OnColorHSVChanged(DependencyPropertyChangedEventArgs e)
        {
            Color c = ColorHSV.Color;
            c.A = 255;
            SelColor.Fill = new SolidColorBrush(c);
            ChangeSelectorPosition();
        }

        #endregion

        #region Mouse drag

        bool colorDrag = false;

        void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (Wheel.CaptureMouse())
            {
                colorDrag = true;
            }
            Point pt = e.GetPosition(Wheel);
            ColorHSV t = CoordsToColor((int)pt.X, (int)pt.Y, (int)Wheel.ActualWidth/2, (int)Wheel.ActualHeight/2, (int)Wheel.ActualHeight/2);
            t.V = ColorHSV.V;
            t.A = ColorHSV.A;
            ColorHSV = t;
        }

        void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (colorDrag)
            {
                Point pt = e.GetPosition(Wheel);
                ColorHSV t = CoordsToColor((int)pt.X, (int)pt.Y, (int)Wheel.ActualWidth/2, (int)Wheel.ActualHeight/2, (int)Wheel.ActualHeight / 2);
                t.V = ColorHSV.V;
                t.A = ColorHSV.A;
                ColorHSV = t;
            }
        }

        void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            colorDrag = false;
            Wheel.ReleaseMouseCapture();
        }
        #endregion
    }
}
